local CGCustomShader = CGCustomShader or {}
CGCustomShader.__index = CGCustomShader

function CGCustomShader.new()
    local self = setmetatable({}, CGCustomShader)
    self.nexts = {}
    self.inputs = {}
    self.I_ScriptName = nil
    self.I_TextureLists = nil
    self.I_Uniforms = nil
    self.scriptInfo = nil
    self.MeshRenderer = nil
    self.rectMaterial = nil
    self.old_depthStencilState = nil
    self.old_colorBlend = nil
    self.default_vs=[[
        #ifdef GL_ES
        precision highp float;
        #endif
        attribute vec3 attrPos;
        attribute vec2 attrUV;
        uniform   mat4 u_MVP;
        varying vec2 uv;
        void main() {
            gl_Position = u_MVP * vec4(attrPos, 1.0);
            uv = attrUV;
        }
    ]]
    self.default_fs=[[
        #ifdef GL_ES
        precision highp float;
        #endif
        varying vec2 uv;
        float PI = 3.141592;
        
        float atan2(float y, float x)
        {
            bool s = (abs(x) > abs(y));
            if(x==0.0 && y==0.0){
                return 0.0;
            }
            if(s){
                return atan(y,x);
            }else{
                return PI/2.0 - atan(x,y);
            }
        }
    
        void main() {
            vec4 color = vec4(1.0,0.0,0.0,0.5);
            gl_FragColor = color;
        }
    ]]

    return self
end

function CGCustomShader:setInput(index, func)
    self.inputs[index] = func
end

function CGCustomShader:setNext(index, func)
    self.nexts[index] = func
end

function CGCustomShader:getOutput(index)
    return nil
end

function CGCustomShader:createColorBlend()
    local colorBlend = Amaz.ColorBlendState()
    colorBlend.blendConstants = Amaz.Vector4f(0,0,0,0)
    local colorBlendAttach = Amaz.ColorBlendAttachmentState()
    colorBlendAttach.blendEnable = true
    colorBlendAttach.srcColorBlendFactor = Amaz.BlendFactor.ONE
    colorBlendAttach.dstColorBlendFactor = Amaz.BlendFactor.ONE_MINUS_SRC_ALPHA
    colorBlendAttach.ColorBlendOp = Amaz.BlendOp.ADD
    colorBlendAttach.srcAlphaBlendFactor = Amaz.BlendFactor.ONE
    colorBlendAttach.dstAlphaBlendFactor = Amaz.BlendFactor.ONE_MINUS_SRC_ALPHA
    colorBlendAttach.AlphaBlendOp = Amaz.BlendOp.ADD
    colorBlendAttach.colorWriteMask = 15
    local attachmentVec = Amaz.Vector()
    attachmentVec:pushBack(colorBlendAttach)
    colorBlend.attachments = attachmentVec
    return colorBlend
end

function CGCustomShader:createMaterial()
    --createShader
    local vs = Amaz.Shader()
    vs.type = Amaz.ShaderType.VERTEX
    vs.source = self.scriptInfo.vs
    local fs = Amaz.Shader()
    fs.type = Amaz.ShaderType.FRAGMENT
    fs.source = self.scriptInfo.fs
    local shaders = Amaz.Map()
    local shaderList = Amaz.Vector()
    shaderList:pushBack(vs)
    shaderList:pushBack(fs)
    shaders:insert("gles2", shaderList)
    -- render state
    local renderState = Amaz.RenderState()
    --depth state
    if self.old_depthStencilState ~= nil then
        self.depthStencilState = self.old_depthStencilState
    else
        self.depthStencilState = Amaz.DepthStencilState()
        self.depthStencilState.depthTestEnable = true
        self.depthStencilState.depthWriteEnable = true
        self.depthStencilState.stencilTestEnable = true
    end
    renderState.depthstencil = self.depthStencilState
    --colorBlend
    if self.old_colorBlend ~= nil then
        self.colorBlend = self.old_colorBlend
    else
        self.colorBlend = self.createColorBlend()
    end
    renderState.colorBlend = self.colorBlend
    --createPass
    local pass = Amaz.Pass()
    pass.shaders = shaders
    local seman = Amaz.Map()
    seman:insert("attrPos", Amaz.VertexAttribType.POSITION)
    seman:insert("attrUV", Amaz.VertexAttribType.TEXCOORD0)
    pass.semantics = seman
    pass.renderState = renderState
    local xShader = Amaz.XShader()
    xShader.passes:pushBack(pass)
    local rectMaterial = Amaz.Material()
    rectMaterial.xshader = xShader
    local props = Amaz.PropertySheet()
    rectMaterial.properties = props
    return rectMaterial
end

function CGCustomShader:execute(index)
    self.I_ScriptName = self.inputs[1]()
    if self.I_ScriptName ~=nil then
        if pcall(function(name) self.scriptInfo = require(name) end, self.I_ScriptName) then
        else
            self.scriptInfo = {
                ["vs"] = self.default_vs,["fs"] = self.default_fs
            }
        end
    else
        self.scriptInfo = {
            ["vs"] = self.default_vs,["fs"] = self.default_fs
        }
    end
    
    self.I_TextureLists = self.inputs[2]()
    self.I_Uniforms = self.inputs[3]()
    self.MeshRenderer = self.inputs[4]()
    local old_xshader = self.MeshRenderer.material.xshader
    local old_pass = old_xshader.passes:get(0)
    local old_RenderState = old_pass.renderState
    self.old_depthStencilState = old_RenderState.depthstencil
    self.old_colorBlend = old_RenderState.colorBlend
    if self.rectMaterial == nil then
        self.rectMaterial = self:createMaterial()
    end
    self.MeshRenderer.material = self.rectMaterial
    if self.I_TextureLists ~= nil then
        for i, v in ipairs(self.I_TextureLists) do
            self.rectMaterial:setTex("ITex" .. i, v)
        end
    end
    --TODO Uniform
    --[[if self.I_Uniforms ~= nil then
        for i, v in ipairs(self.I_Uniforms) do
            if v["value"] then
                self.rectMaterial:setVec2(v["name"],v["value"])
            end
        end 
    end]]
    
end

function CGCustomShader:update(sys, deltaTime)
    self.I_TextureLists = self.inputs[2]()
    if self.I_TextureLists ~= nil then
        for i=0,3,1 do
            if self.I_TextureLists[i] ~= nil then
                self.rectMaterial:setTex("ITex" .. i, self.I_TextureLists[i])
            end
        end
    end
    if self.nexts[0] ~= nil then
        self.nexts[0]()
    end
end

return CGCustomShader
